import json
import os
import re
import time
from threading import Thread

from src.base.settings import Settings
from src.base.cmd_executor import CmdExecutor
from src.base.datatype import PlaceholderHandler, RobotEvent
from src.conf.robot_conf import RobotConf
from src.conf.conf_manager import ConfManager
from src.logger import Logger
from src.exe_control import ExeControl
from src.utils.tool import Tool
from src.bot.handler import Handler


class Robot:
    def __init__(self, conf: ConfManager, logger: Logger, dls: ExeControl):
        self.__conf = conf
        self.__dls = dls
        self.__logger = logger
        self.__handler = Handler(conf, logger)
        self.__ini: RobotConf = self.__conf.robot_conf
        self.__data_dir = os.path.join(Settings.WORK_PATH, Settings.CONF_DIR, "数据")
        self.executor = CmdExecutor(self.__do_cmd_list)
        self.owners = self.__ini.owners
        self.allow_group_admin = self.__ini.allow_group_admin
        self.group = self.__ini.group
        self.group_cmd = self.__conf.robot_cmd.content
        # 内部参数
        self.online = True
        self.__thread_list = []
        self.__data = {}
        self.load_data()
        self.init_data()

    # 发送消息至群聊
    def send_group_message(self, message: str, group_id=0) -> None:
        if not group_id:
            group_id = self.group
        data = {"group_id": group_id,
                "message": message}
        try:
            self.__handler.do_cmd("send_group_msg", data)
        except Exception:
            return

    # 设置群成员群昵称
    def set_group_card(self, user_id: int, card: str, group_id=0) -> None:
        if not group_id:
            group_id = self.group
        data = {"group_id": group_id,
                "user_id": user_id,
                "card": card}
        try:
            self.__handler.do_cmd("set_group_card", data)
        except Exception:
            return

    # 检查存在的记录并设置群名片
    def check_and_set_card(self) -> None:
        # 默认空字符串时不进行操作
        if not self.__ini.record_rename:
            return
        # 未连接时不进行操作
        if not self.__handler.alive:
            return
        record_dict = self.__data["cmdTimes"].get(self.__ini.record_rename)
        # 没有找到匹配记录时不操作
        if not record_dict:
            self.__logger.add_log("[DLS][WARN] 指定记录还不存在，无法自动修改群昵称")
            return
        mem_info_list = self.get_group_mem_list()
        for mem_info in mem_info_list:
            user_id = mem_info["user_id"]
            card = mem_info["card"]
            if record_dict.get(str(user_id)):
                ret: list = record_dict[str(user_id)].get("记录值", [])
                if len(ret) > 0 and ret[0] != card:
                    time.sleep(0.01)
                    self.set_group_card(user_id, ret[0])
        self.__logger.add_log("[DLS] 自动修改群昵称已完成")

    # 获取群成员列表
    def get_group_mem_list(self, group_id=0) -> list:
        if not group_id:
            group_id = self.group
        data = {"group_id": group_id}
        try:
            ret = self.__handler.do_cmd("get_group_member_list", data)["data"]
            if type(ret) is not list:
                return []
            return ret
        except Exception:
            return []

    # 获取群成员id列表
    def get_group_mem_id_list(self, group_id=0) -> list:
        if not group_id:
            group_id = self.group
        mem_info_list = self.get_group_mem_list(group_id)
        mem_id_list = []
        for mem_info in mem_info_list:
            mem_id_list.append(mem_info["user_id"])
        return mem_id_list

    # 处理服务器日志转发至群中
    def deal_robot_log(self) -> None:
        combine_log = self.__logger.get_robot_log()
        if combine_log.length:
            self.send_group_message(combine_log.text)

    # 处理所有内置命令
    def deal_cmd_log(self) -> None:
        # （Minimouse添加了在发送消息的同时执行指令的功能）
        for cmd_list in self.__logger.get_cmd_lists():
            self.executor.submit(cmd_list)

    # 处理QQ消息事件
    def deal_event_log(self) -> None:
        # 获取队列大小并执行
        size = self.__handler.event.qsize()
        for _ in range(size):
            event: RobotEvent = self.__handler.event.get()
            try:
                # 判断是否为群消息
                if event.post_type == "message":
                    if event.message_type == "group":
                        # 判断是否为自己发的消息
                        if event.sender.user_id == self.__handler.bot_qq:
                            continue
                        text: str = event.raw_message
                        # 获取发送者信息
                        nickname = event.sender.nickname
                        card_name = event.sender.card_name
                        user_id = event.sender.user_id
                        is_admin = event.sender.is_admin()
                        group_id = event.group_id
                        # 判断是否为本群的消息
                        if group_id != self.group:
                            if user_id in self.owners \
                                    or (self.allow_group_admin and is_admin):
                                if text == "注册机器人":
                                    self.reset_group_id(group_id)
                            continue
                        # 校验玩家群昵称并修改
                        if self.__ini.record_rename:
                            ret = self.search_data(self.__ini.record_rename, str(user_id))
                            if len(ret) > 0 and ret[0] != card_name:
                                self.set_group_card(user_id, ret[0])
                                card_name = ret[0]
                        # 检索每一条命令
                        for robot_cmd in self.group_cmd:
                            # 判断权限是否满足
                            if not (robot_cmd.get("全员可用") or user_id in self.owners
                                    or user_id in robot_cmd.get("指定成员可用", [])
                                    or (self.allow_group_admin and is_admin)):
                                continue
                            # 判断命令是否符合格式
                            cmd_header: str = robot_cmd.get("识别头")
                            cmd_reg: str = robot_cmd.get("正则")
                            strict_match = robot_cmd.get("严格匹配", False)
                            cmd_reg_res = []
                            # 存在识别头时
                            if cmd_header is not None:
                                # 严格匹配处理
                                if strict_match:
                                    if text != cmd_header:
                                        continue
                                    plain: str = text
                                # 头部匹配处理
                                elif re.match(cmd_header, text):
                                    plain: str = text.replace(cmd_header, "", 1).strip()
                                else:
                                    continue
                            elif cmd_reg and re.findall(cmd_reg, text):
                                # 不存在识别头但正则匹配成功
                                plain: str = text
                            else:
                                continue
                            # 有正则时获取文本匹配结果
                            if cmd_reg:
                                cmd_reg_res = re.findall(cmd_reg, plain)
                            # 生成占位符处理器
                            place_handler = PlaceholderHandler()
                            place_handler.set_re_res(cmd_reg_res)
                            place_handler.add_placeholder("%p", plain)
                            place_handler.add_placeholder("%n", card_name)
                            place_handler.add_placeholder("%i", nickname)
                            place_handler.add_placeholder("%q", str(user_id))
                            # 执行源命令转为列表
                            execute_cmd = robot_cmd.get("执行命令", [])
                            if type(execute_cmd) is str:
                                execute_cmd = [execute_cmd]
                            # 设置关联记录时，启用%r占位符
                            relate_record: str = robot_cmd.get("关联记录", "")
                            rec_list = []
                            if relate_record:
                                relate_record = place_handler.deal_placeholder(relate_record)
                                args = relate_record.split(" ", 1)
                                if len(args) != 2:
                                    self.send_group_message("请指定用户ID")
                                else:
                                    rec_list = self.search_data(args[0], args[1].strip())
                            # 替换占位符生成命令列表
                            cmd_list = []
                            for cmd in execute_cmd:
                                cmd = place_handler.deal_placeholder(cmd)
                                # 处理%r占位符
                                if re.search("%r", cmd):
                                    if rec_list:
                                        for rec in rec_list:
                                            cmd_list.append(cmd.replace("%r", rec))
                                else:
                                    cmd_list.append(cmd)
                            # 是否显示执行命令
                            if self.__conf.conf.show_dls_execute:
                                cmd_text = ', '.join(cmd_list).replace('\n', '\\n')
                                if cmd_text:
                                    self.__logger.add_log(f"[DLS][RES] 执行命令: {cmd_text}")
                            if robot_cmd.get("记录"):
                                record_text = plain
                                if robot_cmd.get("记录值"):
                                    record_text = place_handler.deal_placeholder(robot_cmd["记录值"])
                                self.count_and_do_cmd(robot_cmd, record_text, str(user_id), cmd_list)
                            else:
                                self.executor.submit(cmd_list)
                elif event.post_type == "notice":
                    # 退群清理群记录
                    if event.notice_type == "group_decrease":
                        if event.group_id != self.group:
                            continue
                        user_id: int = event.user_id
                        self.clear_data(str(user_id))
            except Exception as err:
                self.__logger.add_log(f"[DLS][ERROR] 处理消息发生异常！错误: {err}")

    # 事件处理
    def deal_all(self) -> None:
        # 开始逻辑循环
        while self.online:
            time.sleep(0.4)
            self.deal_cmd_log()
            # ws连接断开时挂起
            if not self.__handler.alive:
                continue
            self.deal_robot_log()
            self.deal_event_log()

    def load_data(self) -> None:
        """从文件加载数据"""
        # 数据文件夹不存在时创建
        if not os.path.exists(self.__data_dir):
            os.mkdir(self.__data_dir)
        file_path = os.path.join(self.__data_dir, "命令次数记录.json")
        # 文件不存在时不加载
        if not os.path.exists(file_path):
            return
        try:
            with open(file_path, "rb") as f:
                self.__data = json.loads(f.read())
        except json.decoder.JSONDecodeError:
            print("[DLS][ERROR] 数据文件格式有误！无法加载")

    def init_data(self) -> None:
        """初始化数据对象"""
        if not self.__data.get("cmdTimes"):
            self.__data["cmdTimes"] = {}
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            if not self.__data["cmdTimes"].get(record):
                self.__data["cmdTimes"][record] = {}

    def search_data(self, record: str, user_id: str) -> list[str]:
        """查询记录中指定用户的内容"""
        if self.__data["cmdTimes"][record].get(user_id):
            ret: list = self.__data["cmdTimes"][record][user_id]["记录值"]
            return ret
        return []

    def search_all_data(self, record: str, id_or_name: str) -> list[dict]:
        """根据ID或记录名寻找内容"""
        ret = []
        record_data = self.__data["cmdTimes"].get(record, {})
        if record_data.get(id_or_name):
            # 根据id寻找
            rec: dict = record_data[id_or_name].get("记录值", [])
            ret.append({"ID": id_or_name, "记录值": rec})
        else:
            # 根据记录值寻找
            for user in record_data:
                rec = record_data[user].get("记录值", [])
                if id_or_name in rec:
                    ret.append({"ID": user, "记录值": rec})
        return ret

    def reset_data(self, record: str, qq: str) -> bool:
        """重置记录中指定用户的内容"""
        if self.search_data(record, qq):
            self.__data["cmdTimes"][record][qq] = {"次数": 0, "记录值": []}
            self.save_data()
            return True
        return False

    def clear_data(self, user_id: str):
        """重置记录并执行回调命令"""
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            player_names: list = self.search_data(record, user_id)
            # 退群触发相关命令
            self.__leave_callback_cmd(robot_cmd, player_names)
            if robot_cmd.get("退群重置") and player_names:
                self.send_group_message(f"{user_id}记录移除: {', '.join(player_names)}")
                self.reset_data(record, user_id)

    def save_data(self) -> None:
        """保存记录至文件"""
        file_path = os.path.join(self.__data_dir, "命令次数记录.json")
        self.save_data_to_file("cmdTimes", self.__data["cmdTimes"], file_path)

    # 命令记录与执行
    def count_and_do_cmd(self, robot_cmd: dict, record_text: str, user_qq: str, cmd_list: list) -> None:
        limit_times = robot_cmd.get("限制次数", 0)
        # 用户初始化
        if not self.__data["cmdTimes"][robot_cmd["记录"]].get(user_qq):
            self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = \
                {"次数": 0, "记录值": []}
        user_dict: dict = self.__data["cmdTimes"][robot_cmd["记录"]][user_qq]
        # 判断与计数
        if limit_times == 0:
            self.send_group_message("记录还未启用！")
        elif user_dict["次数"] < limit_times:
            if self.search_all_data(robot_cmd["记录"], record_text):
                self.send_group_message("重复的记录, 记录被取消！")
            # 命令执行成功时绑定
            elif self.__do_cmd_list(cmd_list):
                user_dict["次数"] += 1
                user_dict["记录值"].append(record_text)
                self.send_group_message(f'ID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')
                # 保存记录至文件
                self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = user_dict
                self.save_data()
                # 自动修改群昵称
                if self.__ini.record_rename:
                    self.set_group_card(int(user_qq), record_text)
            else:
                self.send_group_message("命令执行失败, 记录被取消！")
        elif limit_times == -1 or user_qq in self.owners:
            self.executor.submit(cmd_list)
            user_dict["次数"] += 1
            user_dict["记录值"].append(record_text)
            self.send_group_message(f'ID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')
            # 保存记录至文件
            self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = user_dict
            self.save_data()
        else:
            self.send_group_message(f'命令次数已用完！\nID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')

    # 移除指定记录
    def times_reset(self, record: str, qq: str) -> str:
        if self.reset_data(record, qq):
            return "移除记录成功"
        else:
            return "移除失败，请使用正确的用户ID"

    # 查询指定记录
    def times_search(self, record: str, id_or_name: str) -> str:
        ret_list = []
        data = self.search_all_data(record, id_or_name)
        if not data:
            return f"未找到相关记录"
        for item in data:
            ret_list.append(f'ID: {item["ID"]}\n记录值: {", ".join(item["记录值"])}')
        return "\n".join(ret_list)

    # 清理退群人员的记录并执行回调命令
    def clear_left_member(self):
        mem_id_list = self.get_group_mem_id_list()
        # 获取列表异常时停止清理
        if not mem_id_list:
            self.send_group_message("获取成员列表异常，无法清理")
            return
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            all_data = self.__data["cmdTimes"][record]
            for user_id in all_data:
                if int(user_id) in mem_id_list:
                    continue
                rec_list: list = self.search_data(record, user_id)
                # 退群触发
                self.__leave_callback_cmd(robot_cmd, rec_list)
                if robot_cmd.get("退群重置") and rec_list:
                    self.reset_data(record, user_id)
                    self.send_group_message(f"ID: {user_id}\n记录移除: {', '.join(rec_list)}")
        self.send_group_message("清理已完成")

    def __leave_callback_cmd(self, robot_cmd, rec_list):
        # 退群触发相关命令
        if robot_cmd.get("退群触发"):
            callback_cmd = robot_cmd.get("退群触发", [])
            if type(callback_cmd) is str:
                callback_cmd = [callback_cmd]
            cmd_list = []
            for cmd in callback_cmd:
                if re.search("%r", cmd):
                    for rec in rec_list:
                        cmd_list.append(cmd.replace("%r", rec))
                else:
                    cmd_list.append(cmd)
            self.executor.submit(cmd_list)

    # 重设群ID并保存到文件
    def reset_group_id(self, group_id: int):
        self.group = group_id
        self.__ini.group = group_id
        self.__ini.save()
        self.send_group_message("成功注册到此群组！")

    # 文件的保存
    def save_data_to_file(self, data_key, data_content, file_path: str) -> None:
        data_dict = {data_key: data_content}
        try:
            with open(file_path, "w", encoding="utf-8") as f:
                f.write(json.dumps(data_dict, sort_keys=True, indent=2, ensure_ascii=False))
        except Exception as err:
            self.__logger.add_log(f"[DLS][ERROR] 文件{file_path}保存失败！错误原因: {err}！")

    # 执行命令
    def __do_cmd_list(self, cmd_list: list) -> bool:
        # 判断命令链是否执行成功
        all_cmd_success = True
        for cmd in cmd_list:
            # 被禁用的命令将被屏蔽
            for limit_cmd in self.__conf.conf.limit_dls_cmd:
                if re.match(limit_cmd, cmd):
                    cmd = ""
            # 开始执行命令
            if cmd == "start":
                self.__dls.exe_start()
            elif cmd == "stop":
                self.__dls.exe_stop()
            elif cmd == "restart":
                self.__dls.exe_restart()
            elif re.match("status", cmd):
                content = cmd.replace("status", "", 1).strip()
                self.send_group_message(Tool.get_status(text=content))
            elif re.match("sys", cmd):
                sys_cmd = cmd.replace("sys", "", 1).strip()
                os.system(sys_cmd)
            elif re.match("wait", cmd):
                wait_time = cmd.replace("wait", "", 1).strip()
                try:
                    time.sleep(int(wait_time))
                except ValueError:
                    pass
            elif re.match("group", cmd):
                group_text = cmd.replace("group", "", 1).strip()
                self.__logger.add_robot_msg(group_text)
            elif re.match("cmd", cmd):
                cmd = cmd.replace("cmd", "", 1).strip()
                if not self.__dls.execute_cmd(cmd):
                    all_cmd_success = False
            elif re.match("chatJava", cmd):
                msg_text = cmd.replace("chatJava", "", 1).strip()
                raw_json = {"text": msg_text}
                cmd = f"tellraw @a {json.dumps(raw_json)}".replace("\'", "\"")
                if not self.__dls.execute_cmd(cmd, mute=True):
                    all_cmd_success = False
            elif re.match("chat", cmd):
                msg_text = cmd.replace("chat", "", 1).strip()
                raw_json = {"rawtext": [{"text": msg_text}]}
                cmd = f"tellraw @a {json.dumps(raw_json)}".replace("\'", "\"")
                if not self.__dls.execute_cmd(cmd, mute=True):
                    all_cmd_success = False
            elif re.match("motdpe", cmd):
                cmd = cmd.replace("motdpe", "", 1).strip()
                self.send_group_message(Tool.motd_pe(cmd))
            elif re.match("blackbe", cmd):
                cmd = cmd.replace("blackbe", "", 1).strip()
                self.send_group_message(Tool.black_be(cmd))
            elif re.match("xuid", cmd):
                cmd = cmd.replace("xuid", "", 1).strip()
                self.send_group_message(Tool.xuid_search(cmd))
            elif re.match("timesReset", cmd):
                cmd = cmd.replace("timesReset", "", 1).strip()
                args = cmd.split(" ", 1)
                if len(args) != 2:
                    self.send_group_message("请指定用户ID")
                    all_cmd_success = False
                self.send_group_message(self.times_reset(args[0], args[1].strip()))
            elif re.match("timesSearch", cmd):
                cmd = cmd.replace("timesSearch", "", 1).strip()
                args = cmd.split(" ", 1)
                if len(args) != 2:
                    self.send_group_message("请指定用户ID")
                    all_cmd_success = False
                self.send_group_message(self.times_search(args[0], args[1].strip()))
            elif re.match("clearLeftMem", cmd):
                self.clear_left_member()
            elif re.match("startBak", cmd):
                self.__dls.bak_world()
            elif re.match("dlsload", cmd):
                self.__conf.reload()
                self.reload()
        return all_cmd_success

    # 启动线程
    def start(self) -> None:
        th = Thread(target=self.deal_all, daemon=True)
        th.start()
        self.__thread_list.append(th)
        # 未启用时不启动ws连接
        if not self.__ini.on:
            return
        self.__handler.start()
        # 启动时自动修改群昵称
        self.check_and_set_card()

    # 关闭线程
    def stop(self) -> None:
        # 关闭线程
        self.online = False
        for th in self.__thread_list:
            try:
                th.join(5)
            except Exception as e:
                print(f"\033[31m[DLS][ERROR] {th}异常的线程关闭！错误：{e}\033[0m")
        self.__thread_list.clear()

    # 重加载配置
    def reload(self) -> None:
        self.owners = self.__ini.owners
        self.allow_group_admin = self.__ini.allow_group_admin
        self.group = self.__ini.group
        self.group_cmd = self.__conf.robot_cmd.content
        self.load_data()
        self.init_data()
        self.__handler.reload()
        self.check_and_set_card()
